/*
 * Created by SharpDevelop.
 * User: David
 * Date: 2006-11-25
 * Time: 17:54
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */

using System;
using System.Collections.Generic;

namespace tools
{
	/// <summary>
	/// Reads a text based file into a string/string Dictionary. You can then get information by calling
	/// methods with parameters. Example: Say you have a file (config.txt) with content
	/// 
	/// <para>
	/// #
	/// # File name of sprite map
	/// #
	/// sprite_map_file resources\spritemaps\spritemap.bmp
	/// #
	/// # Animation sequence
	/// #
	/// animation_sequence_1 0,1,2,3,4
	/// #
	/// # Starting direction
	/// #
	/// start_direction 0,3
	/// </para>
	/// 
	/// You can now retrieve the file name, the sequence, and starting direction through
	/// <code>	Config config = new Config();
	/// 		Result result = null;
	/// 		result = config.Open("config.txt");
	/// 		string spriteMapFileName = "";
	/// 		result = config.GetString("sprite_map_file", ref spriteMapFileName);
	/// 		List<int> list = new List<int>();
	/// 		result = config.GetIntList("animation_sequence", list, false);
	/// 		float direction = 0.0f;
	/// 		result = config.GetFloat("start_direction", ref direction);</code>
	/// 
	/// Remember though to check the result between calls. For further details, see class ToolsResult
	/// </summary>
	public class Config
	{
		#region members
		
		/// <summary>
		/// Reads files as lines of text into a List<string>
		/// </summary>
		private ListFileTool 					m_ListFileTool;
		/// <summary>
		/// Stores the content of the last read file, with the parameter as key, and the value as ... value
		/// </summary>
		private Dictionary<string, string>		m_FileContents;
		
		/// <summary>
		/// Stores the path to the directory this program was started from
		/// </summary>
		private readonly string 				m_Path;
		/// <summary>
		/// Stores the name of the last read file, so we can make sufficient error reports in case a config file is
		/// badly formatted
		/// </summary>
		private string 							m_LastReadFile;
		
		#endregion
		
		#region constructors
		
		/// <summary>
		/// Constructor, initializes members
		/// </summary>
		public Config()
		{
			m_ListFileTool = new ListFileTool();
			m_FileContents = new Dictionary<string, string>();
			
			m_Path = m_ListFileTool.GetDirectoryOfFile("dat");
			m_LastReadFile = "";
		}
		
		#endregion
		
		#region methods
		
		/// <summary>
		/// Gets the value at the asked parameter as an int
		/// </summary>
		/// <param name="param">The parameter to look for</param>
		/// <param name="theValue">This will be filled with the int at the given parameter</param>
		/// <returns>ToolsResult object reporting success status</returns>
		public ToolsResult GetInt(string param, ref int theValue)
		{
			string valueAsString = "";
			ToolsResult result = GetString(param, ref valueAsString);
			
			if(!result.m_Failed)
			{
				try
				{
					theValue = int.Parse(valueAsString);
				}
				catch
				{
					result = ReportError(param, "int");
				}
			}
			
			return result;
		}
		
		/// <summary>
		/// Gets the value at the asked parameter as a float
		/// </summary>
		/// <param name="param">The parameter to look for</param>
		/// <param name="theValue">This will be filled with the float at the given parameter</param>
		/// <returns>ToolsResult object reporting success status</returns>
		public ToolsResult GetFloat(string param, ref float theValue)
		{
			string valueAsString = "";
			ToolsResult result = GetString(param, ref valueAsString);
			
			if(!result.m_Failed)
			{
				try
				{
					theValue = float.Parse(valueAsString);
				}
				catch
				{
					result = ReportError(param, "float");
				}
			}
			
			return result;
		}
		
		/// <summary>
		/// Gets the value at the asked parameter as a bool
		/// </summary>
		/// <param name="param">The parameter to look for</param>
		/// <param name="theValue">This will be filled with the bool at the given parameter</param>
		/// <returns>ToolsResult object reporting success status</returns>
		public ToolsResult GetBool(string param, ref bool theValue)
		{
			string valueAsString = "";
			ToolsResult result = GetString(param, ref valueAsString);
			
			if(!result.m_Failed)
			{
				if(valueAsString.Equals("true"))
				{
					theValue = true;
				}
				else
				{
					if(valueAsString.Equals("false"))
					{
						theValue = false;
					}
					else
					{
						result = ReportError(param, "bool");
					}
				}
			}
			
			return result;
		}
		
		/// <summary>
		/// Gets the value at the asked parameter as a string
		/// </summary>
		/// <param name="param">The parameter to look for</param>
		/// <param name="theValue">This will be filled with the string at the given parameter</param>
		/// <returns>ToolsResult object reporting success status</returns>
		public ToolsResult GetString(string param, ref string theValue)
		{
			ToolsResult result = null;
			
			if(m_FileContents.ContainsKey(param))
			{
				theValue = m_FileContents[param];
			}
			else
			{
				result = this.ReportError(param, "string");
			}
			
			if(result == null)
			{
				result = new ToolsResult();
			}
			
			return result;
		}
		
		/// <summary>
		/// Gets the array of int found at the asked parameter, and loads them into the List<int>, either
		/// overwriting it, or appending to it.
		/// </summary>
		/// <param name="param">The parameter to look for</param>
		/// <param name="array">The List to load the array of int into</param>
		/// <param name="append">If this is true, the int array will be appended to the List, if false, the List will be overwritten</param>
		/// <returns>True if the operation was successful, otherwise false</returns>
		public ToolsResult GetIntList(string param, List<int> array, bool append)
		{
			if(!append)
			{
				array.Clear();
			}
			
			string valueAsString = "";
			ToolsResult result = GetString(param, ref valueAsString);
			
			if(!result.m_Failed)
			{
				string[] theNumbersAsStrings = valueAsString.Split(",".ToCharArray());
				List<int> tempList = new List<int>();
				int aValue = 0;
				
				for(int i = 0; i < theNumbersAsStrings.Length; i++)
				{
					try
					{
						aValue = int.Parse(theNumbersAsStrings[i]);
						tempList.Add(aValue);
					}
					catch
					{
						result = ReportError(param, "List<int>");
						i = theNumbersAsStrings.Length;
					}
				}
				
				if(!result.m_Failed)
				{
					foreach(int it in tempList)
					{
						array.Add(it);
					}
				}
			}
			
			return result;
		}
		
		/// <summary>
		/// Attempts to load the content of a file into the Config's memory, for retrieval of specific data.
		/// </summary>
		/// <param name="fileName">The name of the file to load, path is relative to the last used directory (the directory the exe is started in if not manually changed)</param>
		/// <returns>True if the operation was successfull (i.e. the file was found), otherwise false</returns>
		public ToolsResult Open(string fileName)
		{
			//first, restore the path, and clear the Dictionary FileContent and the string LastReadFile
			m_ListFileTool.GetDirectoryOfFile(m_Path);
			m_FileContents.Clear();
			m_LastReadFile = "";
			//then load the file asked for into a list, overwrite
			List<string> fileStrings = new List<string>();
			ToolsResult result = m_ListFileTool.LoadFileToList(fileName, fileStrings, false);
			
			//if the file was loaded, store the file name in LastReadFile for possible error reporting
			if(!result.m_Failed)
			{
				m_LastReadFile = fileName;
			}
			
			//now, filter out all the comments in the file
			for(int i = 0; i < fileStrings.Count; i++)
			{
				if(fileStrings[i].StartsWith("#"))
				{
					fileStrings.RemoveAt(i);
					i--;
				}
			}
			
			//so now, we only have the lines that are actual config-lines, with a parameter and a value
			//let's split those into two parts, and put them in the Dictionary FileContent
			string[] parts = new string[2];
			
			for(int i = 0; i < fileStrings.Count; i++)
			{
				parts = fileStrings[i].Split(" ".ToCharArray(), 2);
				
				if(!parts[0].Equals("") && !parts[1].Equals(""))
				{
					m_FileContents.Add(parts[0], parts[1]);
				}
			}
			
			//finally, return the outcome, success or failure
			return result;
		}
		
		#endregion
		
		#region private methods
		
		/// <summary>
		/// Returns a ToolsResult object with an error report
		/// </summary>
		/// <param name="param">The parameter that caused the error</param>
		/// <param name="paramType">The type that was being asked for when the parameter was sent</param>
		/// <returns>The ToolsResult object with the error report in</returns>
		private ToolsResult ReportError(string param, string paramType)
		{
			ToolsResult result = new ToolsResult();
			
			result.m_Failed = true;
			result.m_Header = "Config Error:";
			result.m_Result = "Error reading file '" + m_LastReadFile + "'. Parameter was '" + param + "', and the asked type was '" + paramType + "'.\n" +
				"If the asked type was 'string', it means that the parameter asked could not be found.\n"+
				"Otherwise it means that the parameter existed, but the value was badly formatted.";
			
			return result;
		}
		
		#endregion
	}
}
